home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-desktop-9.10-i386-PL.iso / casper / filesystem.squashfs / usr / share / hplip / ui4 / devmgr5.py < prev    next >
Encoding:
Python Source  |  2009-10-09  |  77.0 KB  |  2,191 lines

  1. # -*- coding: utf-8 -*-
  2. #
  3. # (c) Copyright 2001-2009 Hewlett-Packard Development Company, L.P.
  4. #
  5. # This program is free software; you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation; either version 2 of the License, or
  8. # (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with this program; if not, write to the Free Software
  17. # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  18. #
  19. # Authors: Don Welch
  20. #
  21.  
  22. #from __future__ import generators
  23.  
  24. # Std Lib
  25. import sys
  26. import time
  27. import os
  28. import gzip
  29. import select
  30. import struct
  31.  
  32. # Local
  33. from base.g import *
  34. from base import device, utils, pml, maint, models, pkit
  35. from prnt import cups
  36. from base.codes import *
  37. from ui_utils import *
  38. import hpmudext
  39.  
  40. # Qt
  41. from PyQt4.QtCore import *
  42. from PyQt4.QtGui import *
  43.  
  44. # dbus
  45. try:
  46.     import dbus
  47.     from dbus.mainloop.qt import DBusQtMainLoop
  48.     from dbus import lowlevel
  49. except ImportError:
  50.     log.error("Unable to load DBus libraries. Please check your installation and try again.")
  51.     sys.exit(1)
  52.  
  53. import warnings
  54. # Ignore: .../dbus/connection.py:242: DeprecationWarning: object.__init__() takes no parameters
  55. # (occurring on Python 2.6/dBus 0.83/Ubuntu 9.04)
  56. warnings.simplefilter("ignore", DeprecationWarning)
  57.  
  58.  
  59. # Main form
  60. from devmgr5_base import Ui_MainWindow
  61.  
  62. # Aux. dialogs
  63. from faxsetupdialog import FaxSetupDialog
  64. from plugindialog import PluginDialog
  65. from firmwaredialog import FirmwareDialog
  66. from aligndialog import AlignDialog
  67. from printdialog import PrintDialog
  68. from makecopiesdialog import MakeCopiesDialog
  69. from sendfaxdialog import SendFaxDialog
  70. from fabwindow import FABWindow
  71. from devicesetupdialog import DeviceSetupDialog
  72. from printtestpagedialog import PrintTestPageDialog
  73. from infodialog import InfoDialog
  74. from cleandialog import CleanDialog
  75. from colorcaldialog import ColorCalDialog
  76. from linefeedcaldialog import LineFeedCalDialog
  77. from pqdiagdialog import PQDiagDialog
  78. from nodevicesdialog import NoDevicesDialog
  79. from aboutdialog import AboutDialog
  80.  
  81. # Other forms and controls
  82. from settingsdialog import SettingsDialog
  83. from printsettingstoolbox import PrintSettingsToolbox
  84.  
  85.  
  86. # all in seconds
  87. MIN_AUTO_REFRESH_RATE = 5
  88. MAX_AUTO_REFRESH_RATE = 60
  89. DEF_AUTO_REFRESH_RATE = 30
  90.  
  91.  
  92. device_list = {}    # { Device_URI : device.Device(), ... }
  93. model_obj = models.ModelData() # Used to convert dbus xformed data back to plain Python types
  94.  
  95.  
  96. # ***********************************************************************************
  97. #
  98. # ITEM/UTILITY UI CLASSES
  99. #
  100. # ***********************************************************************************
  101.  
  102.  
  103. class FuncViewItem(QListWidgetItem):
  104.     def __init__(self, parent, text, pixmap, tooltip_text, cmd):
  105.         QListWidgetItem.__init__(self, QIcon(pixmap), text, parent)
  106.         self.tooltip_text = tooltip_text
  107.         self.cmd = cmd
  108.  
  109. # XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  110.  
  111. class DeviceViewItem(QListWidgetItem):
  112.     def __init__(self, parent, text, pixmap, device_uri, is_avail=True):
  113.         QListWidgetItem.__init__(self, QIcon(pixmap), text, parent)
  114.         self.device_uri = device_uri
  115.         self.is_avail = is_avail
  116.         self.setTextAlignment(Qt.AlignHCenter)
  117.  
  118. # XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  119.  
  120. class PluginInstall(QObject):
  121.     def __init__(self, parent, plugin_type, plugin_installed):
  122.         self.parent = parent
  123.         self.plugin_type = plugin_type
  124.         self.plugin_installed = plugin_installed
  125.  
  126.  
  127.     def exec_(self):
  128.         install_plugin = True
  129.  
  130.         if self.plugin_installed:
  131.             install_plugin = QMessageBox.warning(self.parent,
  132.                                 self.parent.windowTitle(),
  133.                                 self.__tr("<b>The HPLIP plugin is already installed.</b><p>Do you want to continue and re-install it?"),
  134.                                 QMessageBox.Yes,
  135.                                 QMessageBox.No,
  136.                                 QMessageBox.NoButton) == QMessageBox.Yes
  137.  
  138.         if install_plugin:
  139.             ok, sudo_ok = pkit.run_plugin_command(self.plugin_type == PLUGIN_REQUIRED, self.parent.cur_device.mq['plugin-reason'])
  140.             if not sudo_ok:
  141.                 QMessageBox.critical(self.parent,
  142.                     self.parent.windowTitle(),
  143.                     self.__tr("<b>Unable to find an appropriate su/sudo utility to run hp-plugin.</b><p>Install kdesu, gnomesu, or gksu.</p>"),
  144.                     QMessageBox.Ok,
  145.                     QMessageBox.NoButton,
  146.                     QMessageBox.NoButton)
  147.  
  148.  
  149.     def __tr(self,s,c = None):
  150.         return qApp.translate("DevMgr5",s,c)
  151.  
  152.  
  153.  
  154. # ***********************************************************************************
  155. #
  156. # MAINWINDOW
  157. #
  158. # ***********************************************************************************
  159.  
  160. class DevMgr5(QMainWindow,  Ui_MainWindow):
  161.     def __init__(self,  toolbox_version, initial_device_uri=None,
  162.                  dbus_loop=None, parent=None, name=None, fl=0):
  163.  
  164.         QMainWindow.__init__(self, parent)
  165.  
  166.         log.debug("Initializing toolbox UI (Qt4)...")
  167.         log.debug("HPLIP Version: %s" % prop.installed_version)
  168.  
  169.         self.setupUi(self)
  170.  
  171.         self.toolbox_version = toolbox_version
  172.         self.initial_device_uri = initial_device_uri
  173.         self.device_vars = {}
  174.         self.num_devices = 0
  175.         self.cur_device = None
  176.         self.cur_printer = None
  177.         self.updating = False
  178.         self.init_failed = False
  179.         self.service = None
  180.  
  181.         # User settings
  182.         self.user_settings = UserSettings()
  183.         self.user_settings.load()
  184.         self.user_settings.debug()
  185.         self.cur_device_uri = self.user_settings.last_used_device_uri
  186.  
  187.         # Other initialization
  188.         self.initDBus()
  189.         self.initPixmaps()
  190.         self.initMisc()
  191.         self.initUI()
  192.  
  193.         cups.setPasswordCallback(showPasswordUI)
  194.  
  195.         if not prop.doc_build:
  196.             self.ContentsAction.setEnabled(False)
  197.  
  198.         self.allow_auto_refresh = True
  199.         QTimer.singleShot(0, self.initalUpdate)
  200.  
  201.  
  202.     # ***********************************************************************************
  203.     #
  204.     # INIT
  205.     #
  206.     # ***********************************************************************************
  207.  
  208.     # TODO: Make sbus init mandatory success, else exit
  209.     def initDBus(self):
  210.         self.dbus_loop = DBusQtMainLoop(set_as_default=True)
  211.         self.dbus_avail, self.service, self.session_bus = device.init_dbus(self.dbus_loop)
  212.  
  213.         if not self.dbus_avail:
  214.             log.error("dBus initialization error. Exiting.")
  215.             self.init_failed = True
  216.             return
  217.  
  218.         # Receive events from the session bus
  219.         self.session_bus.add_signal_receiver(self.handleSessionSignal, sender_keyword='sender',
  220.             destination_keyword='dest', interface_keyword='interface',
  221.             member_keyword='member', path_keyword='path')
  222.  
  223.  
  224.     def initPixmaps(self):
  225.         self.func_icons_cached = False
  226.         self.func_icons = {}
  227.         self.device_icons = {}
  228.  
  229.          # Application icon
  230.         self.setWindowIcon(QIcon(load_pixmap('hp_logo', '128x128')))
  231.  
  232.         self.fax_icon = load_pixmap("fax2", "other")
  233.  
  234.  
  235.     def initUI(self):
  236.         # Setup device icon list
  237.         self.DeviceList.setSortingEnabled(True)
  238.         self.DeviceList.setContextMenuPolicy(Qt.CustomContextMenu)
  239.         self.setDeviceListViewMode(QListView.IconMode)
  240.  
  241.         self.connect(self.ViewAsIconsAction, SIGNAL("triggered()"), lambda: self.setDeviceListViewMode(QListView.IconMode))
  242.         self.connect(self.ViewAsListAction, SIGNAL("triggered()"), lambda: self.setDeviceListViewMode(QListView.ListMode))
  243.  
  244.         self.connect(self.DeviceList, SIGNAL("customContextMenuRequested(const QPoint &)"), self.DeviceList_customContextMenuRequested)
  245.  
  246.         # Setup main menu
  247.         self.DeviceRefreshAction.setIcon(QIcon(load_pixmap("refresh1", "16x16")))
  248.         self.connect(self.DeviceRefreshAction, SIGNAL("triggered()"), self.DeviceRefreshAction_activated)
  249.  
  250.         self.RefreshAllAction.setIcon(QIcon(load_pixmap("refresh", "16x16")))
  251.         self.connect(self.RefreshAllAction, SIGNAL("triggered()"), self.RefreshAllAction_activated)
  252.  
  253.         self.SetupDeviceAction.setIcon(QIcon(load_pixmap('list_add', '16x16')))
  254.         self.connect(self.SetupDeviceAction, SIGNAL("triggered()"), self.SetupDeviceAction_activated)
  255.  
  256.         self.RemoveDeviceAction.setIcon(QIcon(load_pixmap('list_remove', '16x16')))
  257.         self.connect(self.RemoveDeviceAction, SIGNAL("triggered()"), self.RemoveDeviceAction_activated)
  258.  
  259.         self.PreferencesAction.setIcon(QIcon(load_pixmap('settings', '16x16')))
  260.         self.connect(self.PreferencesAction, SIGNAL("triggered()"), self.PreferencesAction_activated)
  261.  
  262.         self.ContentsAction.setIcon(QIcon(load_pixmap("help", "16x16")))
  263.         self.connect(self.ContentsAction, SIGNAL("triggered()"), self.helpContents)
  264.  
  265.         self.QuitAction.setIcon(QIcon(load_pixmap("quit", "16x16")))
  266.         self.connect(self.QuitAction, SIGNAL("triggered()"), self.quit)
  267.  
  268.         self.connect(self.AboutAction, SIGNAL("triggered()"), self.helpAbout)
  269.  
  270.         self.connect(self.PrintControlPrinterNameCombo, SIGNAL("activated(const QString &)"), self.PrintControlPrinterNameCombo_activated)
  271.         self.connect(self.PrintSettingsPrinterNameCombo, SIGNAL("activated(const QString &)"), self.PrintSettingsPrinterNameCombo_activated)
  272.  
  273.  
  274.          # Init tabs/controls
  275.         self.initActionsTab()
  276.         self.initStatusTab()
  277.         self.initSuppliesTab()
  278.         self.initPrintSettingsTab()
  279.         self.initPrintControlTab()
  280.  
  281.         self.connect(self.Tabs,SIGNAL("currentChanged(int)"),self.Tabs_currentChanged)
  282.  
  283.         # Resize the splitter so that the device list starts as a single column
  284.         self.splitter.setSizes([80, 600])
  285.  
  286.         # Setup the Device List
  287.         self.DeviceList.setIconSize(QSize(60, 60))
  288.         self.connect(self.DeviceList,  SIGNAL("currentItemChanged(QListWidgetItem * ,QListWidgetItem *)"),
  289.                      self.DeviceList_currentChanged)
  290.  
  291.  
  292.     def initMisc(self):
  293.         self.TabIndex = { 0: self.updateActionsTab,
  294.                           1: self.updateStatusTab,
  295.                           2: self.updateSuppliesTab,
  296.                           3: self.updatePrintSettingsTab,
  297.                           4: self.updatePrintControlTab,
  298.                         }
  299.  
  300.         # docs
  301.         self.docs = "http://hplip.sf.net"
  302.  
  303.         if prop.doc_build:
  304.             g = os.path.join(sys_conf.get('dirs', 'doc'), 'index.html')
  305.             if os.path.exists(g):
  306.                 self.docs = "file://%s" % g
  307.  
  308.         # support
  309.         self.support = "https://launchpad.net/hplip"
  310.  
  311.  
  312.  
  313.     def initalUpdate(self):
  314.         if self.init_failed:
  315.             self.close()
  316.             return
  317.  
  318.         self.rescanDevices()
  319.  
  320.         cont = True
  321.         if self.initial_device_uri is not None:
  322.             if not self.activateDevice(self.initial_device_uri):
  323.                 log.error("Device %s not found" % self.initial_device_uri)
  324.                 cont = False
  325.  
  326.         if self.cur_printer:
  327.             self.getPrinterState()
  328.  
  329.             if self.printer_state == cups.IPP_PRINTER_STATE_STOPPED:
  330.                 self.cur_device.sendEvent(EVENT_PRINTER_QUEUE_STOPPED, self.cur_printer)
  331.  
  332.             if not self.printer_accepting:
  333.                 self.cur_device.sendEvent(EVENT_PRINTER_QUEUE_REJECTING_JOBS, self.cur_printer)
  334.  
  335.  
  336.     def activateDevice(self, device_uri):
  337.         log.debug(log.bold("Activate: %s %s %s" % ("*"*20, device_uri, "*"*20)))
  338.         index = 0
  339.         d = self.DeviceList.item(index) #firstItem()
  340.         found = False
  341.  
  342.         while d is not None:
  343.             if d.device_uri == device_uri:
  344.                 found = True
  345.                 self.DeviceList.setSelected(d, True)
  346.                 self.DeviceList.setCurrentItem(d)
  347.                 break
  348.  
  349.             index += 1
  350.             d = self.DeviceList.item(index)
  351.  
  352.         return found
  353.  
  354.  
  355.  
  356.     # ***********************************************************************************
  357.     #
  358.     # UPDATES/NOTIFICATIONS
  359.     #
  360.     # ***********************************************************************************
  361.  
  362.     def handleSessionSignal(self, *args, **kwds):
  363.         if kwds['interface'] == 'com.hplip.Toolbox' and \
  364.             kwds['member'] == 'Event':
  365.  
  366.             log.debug("Handling event...")
  367.             event = device.Event(*args[:6])
  368.             event.debug()
  369.  
  370.             if event.event_code < EVENT_MIN_USER_EVENT:
  371.                 pass
  372.  
  373.             elif event.event_code == EVENT_DEVICE_UPDATE_REPLY:
  374.                 log.debug("EVENT_DEVICE_UPDATE_REPLY (%s)" % event.device_uri)
  375.                 dev = self.findDeviceByURI(event.device_uri)
  376.  
  377.                 if dev is not None:
  378.                     try:
  379.                         self.service.GetStatus(event.device_uri, reply_handler=self.handleStatusReply,
  380.                             error_handler=self.handleStatusError)
  381.  
  382.                     except dbus.exceptions.DBusException, e:
  383.                         log.error("dbus call to GetStatus() failed.")
  384.  
  385.             elif event.event_code == EVENT_USER_CONFIGURATION_CHANGED:
  386.                 log.debug("EVENT_USER_CONFIGURATION_CHANGED")
  387.                 self.user_settings.load()
  388.  
  389.             elif event.event_code == EVENT_HISTORY_UPDATE:
  390.                 log.debug("EVENT_HISTORY_UPDATE (%s)" % event.device_uri)
  391.                 dev = self.findDeviceByURI(event.device_uri)
  392.                 if dev is not None:
  393.                     self.updateHistory(dev)
  394.  
  395.             elif event.event_code == EVENT_SYSTEMTRAY_EXIT:
  396.                 log.debug("EVENT_SYSTEMTRAY_EXIT")
  397.                 log.error("HPLIP Status Service was closed. HPLIP Device Manager will now exit.")
  398.                 self.close()
  399.  
  400.             elif event.event_code == EVENT_RAISE_DEVICE_MANAGER:
  401.                 log.debug("EVENT_RAISE_DEVICE_MANAGER")
  402.                 self.showNormal()
  403.                 self.setWindowState(self.windowState() & ~Qt.WindowMinimized | Qt.WindowActive)
  404.                 self.raise_()
  405.  
  406.             elif event.event_code in (EVENT_DEVICE_START_POLLING,
  407.                                       EVENT_DEVICE_STOP_POLLING,
  408.                                       EVENT_POLLING_REQUEST):
  409.                 pass
  410.  
  411.             else:
  412.                 log.error("Unhandled event: %d" % event.event_code)
  413.  
  414.  
  415.     def handleStatusReply(self, device_uri, data):
  416.         dev = self.findDeviceByURI(device_uri)
  417.         if dev is not None:
  418.             t = {}
  419.             for key in data:
  420.                 value = model_obj.convert_data(str(key), str(data[key]))
  421.                 t.setdefault(key, value)
  422.  
  423.             dev.dq = t.copy()
  424.             for d in dev.dq:
  425.                 dev.__dict__[d.replace('-','_')] = dev.dq[d]
  426.  
  427.             self.updateDevice(dev)
  428.  
  429.  
  430.     def handleStatusError(self, e):
  431.         log.error(str(e))
  432.  
  433.  
  434.     def updateHistory(self, dev=None):
  435.         if dev is None:
  436.             dev = self.cur_device
  437.  
  438.         try:
  439.             self.service.GetHistory(dev.device_uri, reply_handler=self.handleHistoryReply,
  440.                                     error_handler=self.handleHistoryError)
  441.         except dbus.exceptions.DBusException, e:
  442.             log.error("dbus call to GetHistory() failed.")
  443.  
  444.  
  445.     def handleHistoryReply(self, device_uri, history):
  446.         dev = self.findDeviceByURI(device_uri)
  447.         if dev is not None:
  448.             result = []
  449.             history.reverse()
  450.  
  451.             for h in history:
  452.                 result.append(device.Event(*tuple(h)))
  453.  
  454.             try:
  455.                 self.error_code = result[0].event_code
  456.             except IndexError:
  457.                 self.error_code = STATUS_UNKNOWN
  458.  
  459.             dev.error_state = STATUS_TO_ERROR_STATE_MAP.get(self.error_code, ERROR_STATE_CLEAR)
  460.             dev.hist = result
  461.  
  462.             self.updateDevice(dev)
  463.  
  464.  
  465.     def handleHistoryError(self, e):
  466.         log.error(str(e))
  467.  
  468.  
  469.     def sendMessage(self, device_uri, printer_name, event_code, username=prop.username,
  470.                     job_id=0, title=''):
  471.  
  472.         device.Event(device_uri, printer_name, event_code, username,
  473.                     job_id, title).send_via_dbus(self.session_bus)
  474.  
  475.  
  476.     def timedRefresh(self):
  477.         if not self.updating and self.user_settings.auto_refresh and self.allow_auto_refresh:
  478.             log.debug("Refresh timer...")
  479.             self.cleanupChildren()
  480.  
  481.             if self.user_settings.auto_refresh_type == 0:
  482.                 self.requestDeviceUpdate()
  483.             else:
  484.                 self.rescanDevices()
  485.  
  486.  
  487.     # ***********************************************************************************
  488.     #
  489.     # TAB/DEVICE CHANGE SLOTS
  490.     #
  491.     # ***********************************************************************************
  492.  
  493.     def Tabs_currentChanged(self, tab=0):
  494.         """ Called when the active tab changes.
  495.             Update newly displayed tab.
  496.         """
  497.         if self.cur_device is not None:
  498.             self.TabIndex[tab]()
  499.  
  500.     def updateAllTabs(self):
  501.         for tab in self.TabIndex:
  502.             self.TabIndex[tab]()
  503.  
  504.  
  505.     def updateCurrentTab(self):
  506.         log.debug("updateCurrentTab()")
  507.         self.TabIndex[self.Tabs.currentIndex()]()
  508.  
  509.  
  510.  
  511.     # ***********************************************************************************
  512.     #
  513.     # DEVICE ICON LIST/DEVICE UPDATE(S)
  514.     #
  515.     # ***********************************************************************************
  516.  
  517.  
  518.     def DeviceRefreshAction_activated(self):
  519.         self.DeviceRefreshAction.setEnabled(False)
  520.         self.requestDeviceUpdate()
  521.         self.DeviceRefreshAction.setEnabled(True)
  522.  
  523.  
  524.     def RefreshAllAction_activated(self):
  525.         self.rescanDevices()
  526.  
  527.  
  528.     def setDeviceListViewMode(self, mode):
  529.         if mode == QListView.ListMode:
  530.             self.DeviceList.setViewMode(QListView.ListMode)
  531.             self.ViewAsListAction.setEnabled(False)
  532.             self.ViewAsIconsAction.setEnabled(True)
  533.         else:
  534.             self.DeviceList.setViewMode(QListView.IconMode)
  535.             self.ViewAsListAction.setEnabled(True)
  536.             self.ViewAsIconsAction.setEnabled(False)
  537.  
  538.  
  539.     def createDeviceIcon(self, dev=None):
  540.         if dev is None:
  541.             dev = self.cur_device
  542.  
  543.         try:
  544.             dev.icon
  545.         except AttributeError:
  546.             dev.icon = "default_printer"
  547.  
  548.         try:
  549.             self.device_icons[dev.icon]
  550.         except:
  551.             self.device_icons[dev.icon] = load_pixmap(dev.icon, 'devices')
  552.  
  553.         pix = self.device_icons[dev.icon]
  554.  
  555.         w, h = pix.width(), pix.height()
  556.         error_state = dev.error_state
  557.         icon = QPixmap(w, h)
  558.         p = QPainter(icon)
  559.         p.eraseRect(0, 0, icon.width(), icon.height())
  560.         p.drawPixmap(0, 0, pix)
  561.  
  562.         try:
  563.             tech_type = dev.tech_type
  564.         except AttributeError:
  565.             tech_type = TECH_TYPE_NONE
  566.  
  567.         if dev.device_type == DEVICE_TYPE_FAX:
  568.             p.drawPixmap(w - self.fax_icon.width(), 0, self.fax_icon)
  569.  
  570.         if error_state != ERROR_STATE_CLEAR:
  571.             if tech_type in (TECH_TYPE_COLOR_INK, TECH_TYPE_MONO_INK):
  572.                 status_icon = getStatusOverlayIcon(error_state)[0] # ink
  573.             else:
  574.                 status_icon = getStatusOverlayIcon(error_state)[1] # laser
  575.  
  576.             if status_icon is not None:
  577.                 p.drawPixmap(0, 0, status_icon)
  578.  
  579.         p.end()
  580.         return icon
  581.  
  582.  
  583.     def refreshDeviceList(self):
  584.         global devices
  585.         log.debug("Rescanning device list...")
  586.  
  587.         if 1:
  588.             beginWaitCursor()
  589.             self.updating = True
  590.  
  591.             self.setWindowTitle(self.__tr("Refreshing Device List - HP Device Manager"))
  592.             self.statusBar().showMessage(self.__tr("Refreshing device list..."))
  593.  
  594.             self.cups_devices = device.getSupportedCUPSDevices(['hp', 'hpfax'])
  595.  
  596.             current = None
  597.  
  598.             try:
  599.                 adds = []
  600.                 for d in self.cups_devices:
  601.                     if d not in device_list:
  602.                         adds.append(d)
  603.  
  604.                 log.debug("Adds: %s" % ','.join(adds))
  605.  
  606.                 removals = []
  607.                 for d in device_list:
  608.                     if d not in self.cups_devices:
  609.                         removals.append(d)
  610.  
  611.                 log.debug("Removals (1): %s" % ','.join(removals))
  612.  
  613.                 updates = []
  614.                 for d in device_list:
  615.                     if d not in adds and d not in removals:
  616.                         updates.append(d)
  617.  
  618.                 log.debug("Updates: %s" % ','.join(updates))
  619.  
  620.                 for d in adds:
  621.                     log.debug("adding: %s" % d)
  622.                     # Note: Do not perform any I/O with this device.
  623.                     dev = device.Device(d, service=self.service, disable_dbus=False)
  624.  
  625.                     if not dev.supported:
  626.                         log.debug("Unsupported model - removing device.")
  627.                         removals.append(d)
  628.                         continue
  629.  
  630.                     icon = self.createDeviceIcon(dev)
  631.  
  632.                     if dev.device_type == DEVICE_TYPE_FAX:
  633.                         DeviceViewItem(self.DeviceList,  self.__tr("%1 (Fax)").arg(dev.model_ui),
  634.                             icon, d)
  635.                     else:
  636.                         if dev.fax_type:
  637.                             DeviceViewItem(self.DeviceList, self.__tr("%1 (Printer)").arg(dev.model_ui),
  638.                                 icon, d)
  639.                         else:
  640.                             DeviceViewItem(self.DeviceList, dev.model_ui,
  641.                                 icon, d)
  642.  
  643.                     device_list[d] = dev
  644.  
  645.                 log.debug("Removals (2): %s" % ','.join(removals))
  646.  
  647.                 for d in removals:
  648.                     index = self.DeviceList.count()-1
  649.                     item = self.DeviceList.item(index)
  650.                     log.debug("removing: %s" % d)
  651.  
  652.                     try:
  653.                         del device_list[d]
  654.                     except KeyError:
  655.                         pass
  656.  
  657.                     while index >= 0 and item is not None:
  658.                         if item.device_uri == d:
  659.                             self.DeviceList.takeItem(index)
  660.                             break
  661.  
  662.                         index -= 1
  663.                         item = self.DeviceList.item(index)
  664.  
  665.                     qApp.processEvents()
  666.  
  667.                 self.DeviceList.updateGeometry()
  668.                 qApp.processEvents()
  669.  
  670.                 if len(device_list):
  671.                     for tab in self.TabIndex:
  672.                         self.Tabs.setTabEnabled(tab, True)
  673.  
  674.                     if self.cur_device_uri:
  675.                         index = 0
  676.                         item = first_item = self.DeviceList.item(index)
  677.  
  678.                         while item is not None:
  679.                             qApp.processEvents()
  680.                             if item.device_uri == self.cur_device_uri:
  681.                                 current = item
  682.                                 self.statusBar().showMessage(self.cur_device_uri)
  683.                                 break
  684.  
  685.                             index += 1
  686.                             item = self.DeviceList.item(index)
  687.  
  688.                         else:
  689.                             self.cur_device = None
  690.                             self.cur_device_uri = ''
  691.  
  692.                     if self.cur_device is None:
  693.                         i = self.DeviceList.item(0)
  694.                         if i is not None:
  695.                             self.cur_device_uri = i.device_uri
  696.                             self.cur_device = device_list[self.cur_device_uri]
  697.                             current = i
  698.  
  699.                     self.updatePrinterCombos()
  700.  
  701.                     if self.cur_device_uri:
  702.                         #user_conf.set('last_used', 'device_uri',self.cur_device_uri)
  703.                         self.user_settings.last_used_device_uri = self.cur_device_uri
  704.                         self.user_settings.save()
  705.  
  706.                     for d in updates + adds:
  707.                         if d not in removals:
  708.                             self.requestDeviceUpdate(device_list[d])
  709.  
  710.                 else: # no devices
  711.                     self.cur_device = None
  712.                     self.DeviceRefreshAction.setEnabled(False)
  713.                     self.RemoveDeviceAction.setEnabled(False)
  714.                     self.updating = False
  715.                     self.statusBar().showMessage(self.__tr("Press F6 to refresh."))
  716.  
  717.                     for tab in self.TabIndex:
  718.                         self.Tabs.setTabEnabled(tab, False)
  719.  
  720.                     endWaitCursor()
  721.  
  722.                     dlg = NoDevicesDialog(self)
  723.                     dlg.exec_()
  724.  
  725.             finally:
  726.                 self.updating = False
  727.                 endWaitCursor()
  728.  
  729.             if current is not None:
  730.                 self.DeviceList.setCurrentItem(current)
  731.  
  732.             self.DeviceRefreshAction.setEnabled(True)
  733.  
  734.             if self.cur_device is not None:
  735.                 self.RemoveDeviceAction.setEnabled(True)
  736.  
  737.                 self.statusBar().showMessage(self.cur_device_uri)
  738.                 self.updateWindowTitle()
  739.  
  740.  
  741.     def updateWindowTitle(self):
  742.         if self.cur_device.device_type == DEVICE_TYPE_FAX:
  743.                 self.setWindowTitle(self.__tr("HP Device Manager - %1 (Fax)").arg(self.cur_device.model_ui))
  744.         else:
  745.             if self.cur_device.fax_type:
  746.                 self.setWindowTitle(self.__tr("HP Device Manager - %1 (Printer)").arg(self.cur_device.model_ui))
  747.             else:
  748.                 self.setWindowTitle(self.__tr("HP Device Manager - %1").arg(self.cur_device.model_ui))
  749.  
  750.         self.statusBar().showMessage(self.cur_device_uri)
  751.  
  752.  
  753.     def updateDeviceByURI(self, device_uri):
  754.         return self.updateDevice(self.findDeviceByURI(device_uri))
  755.  
  756.  
  757.     def updateDevice(self, dev=None, update_tab=True):
  758.  
  759.         """ Update the device icon and currently displayed tab.
  760.         """
  761.         if dev is None:
  762.             dev = self.cur_device
  763.  
  764.         log.debug("updateDevice(%s)" % dev.device_uri)
  765.  
  766.         item = self.findItem(dev)
  767.  
  768.         if item is not None:
  769.             item.setIcon(QIcon(self.createDeviceIcon(dev)))
  770.  
  771.         if dev is self.cur_device and update_tab:
  772.             self.updatePrinterCombos()
  773.             self.updateCurrentTab()
  774.             self.statusBar().showMessage(self.cur_device_uri)
  775.  
  776.  
  777.     def DeviceList_currentChanged(self, i,  j):
  778.         if i is not None and not self.updating:
  779.             self.cur_device_uri = self.DeviceList.currentItem().device_uri
  780.             self.cur_device = device_list[self.cur_device_uri]
  781.             #user_conf.set('last_used', 'device_uri', self.cur_device_uri)
  782.             self.user_settings.last_used_device_uri = self.cur_device_uri
  783.             self.user_settings.save()
  784.  
  785.             self.updateDevice()
  786.             self.updateWindowTitle()
  787.  
  788.  
  789.     def findItem(self, dev):
  790.         if dev is None:
  791.             dev = self.cur_device
  792.  
  793.         return self.findItemByURI(dev.device_uri)
  794.  
  795.  
  796.     def findItemByURI(self, device_uri):
  797.         index = 0
  798.         item = self.DeviceList.item(index)
  799.  
  800.         while item is not None:
  801.             if item.device_uri == device_uri:
  802.                 return item
  803.  
  804.             index += 1
  805.             item = self.DeviceList.item(index)
  806.  
  807.  
  808.     def findDeviceByURI(self, device_uri):
  809.         try:
  810.             return device_list[device_uri]
  811.         except:
  812.             return None
  813.  
  814.  
  815.     def requestDeviceUpdate(self, dev=None, item=None):
  816.         """ Submit device update request to update thread. """
  817.  
  818.         if dev is None:
  819.             dev = self.cur_device
  820.  
  821.         if dev is not None:
  822.             dev.error_state = ERROR_STATE_REFRESHING
  823.             self.updateDevice(dev, update_tab=False)
  824.  
  825.             self.sendMessage(dev.device_uri, '', EVENT_DEVICE_UPDATE_REQUESTED)
  826.  
  827.  
  828.     def rescanDevices(self):
  829.         """ Rescan and update all devices. """
  830.         if not self.updating:
  831.             self.RefreshAllAction.setEnabled(False)
  832.             try:
  833.                 self.refreshDeviceList()
  834.             finally:
  835.                 self.RefreshAllAction.setEnabled(True)
  836.  
  837.  
  838.     def callback(self):
  839.         qApp.processEvents()
  840.  
  841.  
  842.     # ***********************************************************************************
  843.     #
  844.     # DEVICE LIST RIGHT CLICK
  845.     #
  846.     # ***********************************************************************************
  847.  
  848.     def DeviceList_customContextMenuRequested(self, p):
  849.         d = self.cur_device
  850.  
  851.         if d is not None:
  852.             avail = d.device_state != DEVICE_STATE_NOT_FOUND and d.supported
  853.             printer = d.device_type == DEVICE_TYPE_PRINTER and avail
  854.  
  855.             fax = d.fax_type > FAX_TYPE_NONE and prop.fax_build and d.device_type == DEVICE_TYPE_FAX and \
  856.                 sys.hexversion >= 0x020300f0 and avail
  857.  
  858.             scan = d.scan_type > SCAN_TYPE_NONE and prop.scan_build and \
  859.                             printer and self.user_settings.cmd_scan
  860.  
  861.             cpy = d.copy_type > COPY_TYPE_NONE and printer
  862.  
  863.             popup = QMenu(self)
  864.  
  865.             item = self.DeviceList.currentItem()
  866.             if item is not None:
  867.                 if self.cur_device.error_state != ERROR_STATE_ERROR:
  868.                     if printer:
  869.                         popup.addAction(self.__tr("Print..."), lambda: self.contextMenuFunc(PrintDialog(self, self.cur_printer)))
  870.  
  871.                         if scan:
  872.                             popup.addAction(self.__tr("Scan..."),  lambda: self.contextMenuFunc(self.user_settings.cmd_scan)) #self.ScanButton_clicked)
  873.  
  874.                         if cpy:
  875.                             popup.addAction(self.__tr("Make Copies..."),  lambda: MakeCopiesDialog(self, self.cur_device_uri)) #self.MakeCopiesButton_clicked)
  876.  
  877.                     else: # self.cur_device.device_type == DEVICE_TYPE_FAX:
  878.                         if fax:
  879.                             popup.addAction(self.__tr("Send Fax..."),  lambda: self.contextMenuFunc(SendFaxDialog(self, self.cur_printer, self.cur_device_uri))) #self.SendFaxButton_clicked)
  880.  
  881.                     popup.addSeparator()
  882.  
  883.                 if not self.updating:
  884.                     popup.addAction(self.__tr("Refresh Device"),  self.requestDeviceUpdate) #self.DeviceRefreshAction_activated)
  885.  
  886.             if not self.updating:
  887.                 popup.addAction(self.__tr("Refresh All"),  self.rescanDevices) #self.RefreshAllAction_activated)
  888.  
  889.             popup.addSeparator()
  890.  
  891.             if self.DeviceList.viewMode() == QListView.IconMode:
  892.                 popup.addAction(self.__tr("View as List"), lambda: self.setDeviceListViewMode(QListView.ListMode))
  893.             else:
  894.                 popup.addAction(self.__tr("View as Icons"), lambda: self.setDeviceListViewMode(QListView.IconMode))
  895.  
  896.             popup.exec_(self.DeviceList.mapToGlobal(p))
  897.  
  898.  
  899.     def contextMenuFunc(self, f):
  900.         self.sendMessage('', '', EVENT_DEVICE_STOP_POLLING)
  901.         try:
  902.             try:
  903.                 f.exec_() # Dialog
  904.             except AttributeError:
  905.                 beginWaitCursor()
  906.  
  907.                 if f.split(':')[0] in ('http', 'https', 'file'):
  908.                     log.debug("Opening browser to: %s" % item.cmd)
  909.                     utils.openURL(f)
  910.                 else:
  911.                     self.runExternalCommand(f)
  912.  
  913.                 QTimer.singleShot(1000, self.unlockClick)
  914.         finally:
  915.             self.sendMessage('', '', EVENT_DEVICE_START_POLLING)
  916.  
  917.  
  918.  
  919.     # ***********************************************************************************
  920.     #
  921.     # PRINTER NAME COMBOS
  922.     #
  923.     # ***********************************************************************************
  924.  
  925.  
  926.     def updatePrinterCombos(self):
  927.         self.PrintSettingsPrinterNameCombo.clear()
  928.         self.PrintControlPrinterNameCombo.clear()
  929.  
  930.         if self.cur_device is not None and \
  931.             self.cur_device.supported:
  932.  
  933.             self.cur_device.updateCUPSPrinters()
  934.  
  935.             for c in self.cur_device.cups_printers:
  936.                 self.PrintSettingsPrinterNameCombo.insertItem(0, c.decode("utf-8"))
  937.                 self.PrintControlPrinterNameCombo.insertItem(0, c.decode("utf-8"))
  938.  
  939.             self.cur_printer = unicode(self.PrintSettingsPrinterNameCombo.currentText())
  940.  
  941.  
  942.     def PrintSettingsPrinterNameCombo_activated(self, s):
  943.         self.cur_printer = unicode(s)
  944.         self.updateCurrentTab()
  945.  
  946.  
  947.     def PrintControlPrinterNameCombo_activated(self, s):
  948.         self.cur_printer = unicode(s)
  949.         self.updateCurrentTab()
  950.  
  951.  
  952.  
  953.     # ***********************************************************************************
  954.     #
  955.     # FUNCTIONS/ACTION TAB
  956.     #
  957.     # ***********************************************************************************
  958.  
  959.     def initActionsTab(self):
  960.         self.click_lock = None
  961.         self.ActionsList.setIconSize(QSize(32, 32))
  962.         self.connect(self.ActionsList, SIGNAL("itemClicked(QListWidgetItem *)"),  self.ActionsList_clicked)
  963.         self.connect(self.ActionsList, SIGNAL("itemDoubleClicked(QListWidgetItem *)"),  self.ActionsList_clicked)
  964.  
  965.  
  966.     def updateActionsTab(self):
  967.         beginWaitCursor()
  968.         try:
  969.             self.ActionsList.clear()
  970.  
  971.             d = self.cur_device
  972.  
  973.             if d is not None:
  974.                 avail = d.device_state != DEVICE_STATE_NOT_FOUND and d.supported
  975.                 fax = d.fax_type > FAX_TYPE_NONE and prop.fax_build and d.device_type == DEVICE_TYPE_FAX and \
  976.                     sys.hexversion >= 0x020300f0 and avail
  977.                 printer = d.device_type == DEVICE_TYPE_PRINTER and avail
  978.                 scan = d.scan_type > SCAN_TYPE_NONE and prop.scan_build and \
  979.                         printer and self.user_settings.cmd_scan
  980.                 cpy = d.copy_type > COPY_TYPE_NONE and printer
  981.                 req_plugin = d.plugin == PLUGIN_REQUIRED
  982.                 opt_plugin = d.plugin == PLUGIN_OPTIONAL
  983.  
  984.                 try:
  985.                     back_end, is_hp, bus, model, serial, dev_file, host, zc, port = \
  986.                         device.parseDeviceURI(self.cur_device_uri)
  987.                 except Error:
  988.                     return
  989.  
  990.                 hplip_conf = ConfigParser.ConfigParser()
  991.                 fp = open("/etc/hp/hplip.conf", "r")
  992.                 hplip_conf.readfp(fp)
  993.                 fp.close()
  994.  
  995.                 try:
  996.                     plugin_installed = utils.to_bool(hplip_conf.get("hplip", "plugin"))
  997.                 except ConfigParser.NoOptionError:
  998.                     plugin_installed = False
  999.  
  1000.                 if d.plugin != PLUGIN_NONE:
  1001.                     if req_plugin and plugin_installed:
  1002.                         x = self.__tr("Download and install<br>required plugin (already installed).")
  1003.  
  1004.                     elif req_plugin and not plugin_installed:
  1005.                         x = self.__tr("Download and install<br>required plugin (needs installation).")
  1006.  
  1007.                     elif opt_plugin and plugin_installed:
  1008.                         x = self.__tr("Download and install<br>optional plugin (already installed).")
  1009.  
  1010.                     elif opt_plugin and not plugin_installed:
  1011.                         x = self.__tr("Download and install<br>optional plugin (needs installation).")
  1012.  
  1013.                 else:
  1014.                     x = ''
  1015.  
  1016.                 # TODO: Cache this data structure
  1017.                 #       -- add a field that specifies if the icon should always show, or only when device is avail.
  1018.                 # TODO: Tooltips
  1019.                 # TODO: Right-click icon/list view menu
  1020.  
  1021.                 self.ICONS = [
  1022.  
  1023.                     # PRINTER
  1024.  
  1025.                     (lambda : printer,
  1026.                     self.__tr("Print"),                        # Text
  1027.                     "print",                                   # Icon
  1028.                     self.__tr("Print documents or files."),    # Tooltip
  1029.                     lambda : PrintDialog(self, self.cur_printer)),  # command/action
  1030.  
  1031.                     (lambda :scan,
  1032.                     self.__tr("Scan"),
  1033.                     "scan",
  1034.                     self.__tr("Scan a document, image, or photograph.<br>"),
  1035.                     self.user_settings.cmd_scan),
  1036.  
  1037.                     (lambda : cpy,
  1038.                     self.__tr("Make Copies"),
  1039.                     "makecopies",
  1040.                     self.__tr("Make copies on the device controlled by the PC.<br>"),
  1041.                     lambda : MakeCopiesDialog(self, self.cur_device_uri)),
  1042.  
  1043.                     # FAX
  1044.  
  1045.                     (lambda: fax,
  1046.                     self.__tr("Send Fax"),
  1047.                     "fax",
  1048.                     self.__tr("Send a fax from the PC."),
  1049.                     lambda : SendFaxDialog(self, self.cur_printer, self.cur_device_uri)),
  1050.  
  1051.                     (lambda: fax,
  1052.                     self.__tr("Fax Setup"),
  1053.                     "fax_setup",
  1054.                     self.__tr("Fax support must be setup before you can send faxes."),
  1055.                     lambda : FaxSetupDialog(self, self.cur_device_uri)),
  1056.  
  1057.                     (lambda: fax and self.user_settings.cmd_fab,
  1058.                     self.__tr("Fax Address Book"),
  1059.                     "fab",
  1060.                     self.__tr("Setup fax phone numbers to use when sending faxes from the PC."),
  1061.                     self.user_settings.cmd_fab),
  1062.  
  1063.                     # SETTINGS/TOOLS
  1064.  
  1065.                     (lambda : d.power_settings != POWER_SETTINGS_NONE and avail,
  1066.                     self.__tr("Device Settings"),
  1067.                     "settings",
  1068.                     self.__tr("Your device has special device settings.<br>You may alter these settings here."),
  1069.                     lambda : DeviceSetupDialog(self, self.cur_device_uri)),
  1070.  
  1071.                     (lambda : printer,
  1072.                     self.__tr("Print Test Page"),
  1073.                     "testpage",
  1074.                     self.__tr("Print a test page to test the setup of your printer."),
  1075.                     lambda : PrintTestPageDialog(self, self.cur_printer)),
  1076.  
  1077.                     (lambda : True,
  1078.                     self.__tr("View Printer and Device Information"),
  1079.                     "cups",
  1080.                     self.__tr("View information about the device and all its CUPS queues."),
  1081.                     lambda : InfoDialog(self, self.cur_device_uri)),
  1082.  
  1083.                     (lambda: printer and d.align_type != ALIGN_TYPE_NONE,
  1084.                     self.__tr("Align Cartridges (Print Heads)"),
  1085.                     "align",
  1086.                     self.__tr("This will improve the quality of output when a new cartridge is installed."),
  1087.                     lambda : AlignDialog(self, self.cur_device_uri)),
  1088.  
  1089.                     (lambda: printer and d.clean_type != CLEAN_TYPE_NONE,
  1090.                     self.__tr("Clean Cartridges"),
  1091.                     "clean",
  1092.                     self.__tr("You only need to perform this action if you are<br>having problems with poor printout quality due to clogged ink nozzles."),
  1093.                     lambda : CleanDialog(self, self.cur_device_uri)),
  1094.  
  1095.                     (lambda: printer and d.color_cal_type != COLOR_CAL_TYPE_NONE and d.color_cal_type == COLOR_CAL_TYPE_TYPHOON,
  1096.                     self.__tr("Color Calibration"),
  1097.                     "colorcal",
  1098.                     self.__tr("Use this procedure to optimimize your printer's color output<br>(requires glossy photo paper)."),
  1099.                     lambda : ColorCalDialog(self, self.cur_device_uri)),
  1100.  
  1101.                     (lambda: printer and d.color_cal_type != COLOR_CAL_TYPE_NONE and d.color_cal_type != COLOR_CAL_TYPE_TYPHOON,
  1102.                     self.__tr("Color Calibration"),
  1103.                     "colorcal",
  1104.                     self.__tr("Use this procedure to optimimize your printer's color output."),
  1105.                     lambda : ColorCalDialog(self, self.cur_device_uri)),
  1106.  
  1107.                     (lambda: printer and d.linefeed_cal_type != LINEFEED_CAL_TYPE_NONE,
  1108.                     self.__tr("Line Feed Calibration"),
  1109.                     "linefeed_cal",
  1110.                     self.__tr("Use line feed calibration to optimize print quality<br>(to remove gaps in the printed output)."),
  1111.                     lambda : LineFeedCalDialog(self, self.cur_device_uri)),
  1112.  
  1113.                     (lambda: printer and d.pq_diag_type != PQ_DIAG_TYPE_NONE,
  1114.                     self.__tr("Print Diagnostic Page"),
  1115.                     "pq_diag",
  1116.                     self.__tr("Your printer can print a test page <br>to help diagnose print quality problems."),
  1117.                     lambda : PQDiagDialog(self, self.cur_device_uri)),
  1118.  
  1119.                     (lambda: printer and d.wifi_config >= WIFI_CONFIG_USB_XML and bus == 'usb',
  1120.                      self.__tr("Wireless/wifi setup using USB"),
  1121.                      "wireless",
  1122.                      self.__tr("Configure your wireless capable printer using a temporary USB connection."),
  1123.                      'hp-wificonfig -d %s' % self.cur_device_uri),
  1124.  
  1125.                     # FIRMWARE
  1126.  
  1127.                     (lambda : printer and d.fw_download ,
  1128.                     self.__tr("Download Firmware"),
  1129.                     "firmware",
  1130.                     self.__tr("Download firmware to your printer <br>(required on some devices after each power-up)."),
  1131.                     lambda : FirmwareDialog(self, self.cur_device_uri)),
  1132.  
  1133.                     # PLUGIN
  1134.  
  1135.                     (lambda : printer and req_plugin,
  1136.                     self.__tr("Install Required Plugin"),
  1137.                     "plugin",
  1138.                     x,
  1139.                     lambda : PluginInstall(self, d.plugin, plugin_installed)),
  1140.  
  1141.                     (lambda : printer and opt_plugin,
  1142.                     self.__tr("Install Optional Plugin"),
  1143.                     "plugin",
  1144.                     x,
  1145.                     lambda : PluginInstall(self, d.plugin, plugin_installed)),
  1146.  
  1147.                     # EWS
  1148.  
  1149.                     (lambda : printer and d.embedded_server_type > EWS_NONE and bus == 'net',
  1150.                      self.__tr("Open printer's web page in a browser"),
  1151.                      "ews",
  1152.                      self.__tr("The printer's web page has supply, status, and other information."),
  1153.                      openEWS(host, zc)),
  1154.  
  1155.                     # HELP/WEBSITE
  1156.  
  1157.                     (lambda : True,
  1158.                     self.__tr("Visit HPLIP Support Website"),
  1159.                     "hp_logo",
  1160.                     self.__tr("Visit HPLIP Support Website."),
  1161.                     self.support),
  1162.  
  1163.                     (lambda : True,
  1164.                     self.__tr("Help"),
  1165.                     "help",
  1166.                     self.__tr("View HPLIP help."),
  1167.                     self.docs),
  1168.  
  1169.                 ]
  1170.  
  1171.                 if not self.func_icons_cached:
  1172.                     for filter, text, icon, tooltip, cmd in self.ICONS:
  1173.                         self.func_icons[icon] = load_pixmap(icon, '32x32')
  1174.                     self.func_icons_cached = True
  1175.  
  1176.                 for filter, text, icon, tooltip, cmd in self.ICONS:
  1177.                     if filter is not None:
  1178.                         if not filter():
  1179.                             continue
  1180.  
  1181.                     FuncViewItem(self.ActionsList, text,
  1182.                         self.func_icons[icon],
  1183.                         tooltip,
  1184.                         cmd)
  1185.         finally:
  1186.             endWaitCursor()
  1187.  
  1188.  
  1189.     def ActionsList_clicked(self, item):
  1190.         if item is not None and self.click_lock is not item:
  1191.             self.click_lock = item
  1192.  
  1193.             if item.cmd and callable(item.cmd):
  1194.                 dlg = item.cmd()
  1195.                 self.sendMessage('', '', EVENT_DEVICE_STOP_POLLING)
  1196.                 try:
  1197.                     dlg.exec_()
  1198.                 finally:
  1199.                     self.sendMessage('', '', EVENT_DEVICE_START_POLLING)
  1200.  
  1201.             else:
  1202.                 beginWaitCursor()
  1203.                 if item.cmd.split(':')[0] in ('http', 'https', 'file'):
  1204.                     log.debug("Opening browser to: %s" % item.cmd)
  1205.                     utils.openURL(item.cmd)
  1206.                 else:
  1207.                     self.runExternalCommand(item.cmd)
  1208.  
  1209.             QTimer.singleShot(1000, self.unlockClick)
  1210.  
  1211.  
  1212.     def unlockClick(self):
  1213.         self.click_lock = None
  1214.         endWaitCursor()
  1215.  
  1216.  
  1217.     def ActionsList_customContextMenuRequested(self, p):
  1218.         print p
  1219.         #pass
  1220.  
  1221.  
  1222.     # ***********************************************************************************
  1223.     #
  1224.     # STATUS TAB
  1225.     #
  1226.     # ***********************************************************************************
  1227.  
  1228.     def initStatusTab(self):
  1229.         self.StatusTable.setColumnCount(0)
  1230.         self.status_headers = [self.__tr(""), self.__tr("Status"), self.__tr("Date and Time"),
  1231.                                self.__tr("Code"), self.__tr("Job ID"), self.__tr("Description")]
  1232.  
  1233.  
  1234.     def updateStatusTab(self):
  1235.         self.updateStatusLCD()
  1236.         self.updateStatusTable()
  1237.  
  1238.  
  1239.     def updateStatusLCD(self):
  1240.         if self.cur_device is not None and \
  1241.             self.cur_device.hist and \
  1242.             self.cur_device.supported:
  1243.  
  1244.             dq = self.cur_device.dq
  1245.  
  1246.             if dq.get('panel', 0) == 1:
  1247.                 line1 = dq.get('panel-line1', '')
  1248.                 line2 = dq.get('panel-line2', '')
  1249.             else:
  1250.                 try:
  1251.                     line1 = device.queryString(self.cur_device.hist[0].event_code)
  1252.                 except (AttributeError, TypeError):
  1253.                     line1 = ''
  1254.  
  1255.                 line2 = ''
  1256.  
  1257.             self.drawStatusLCD(line1, line2)
  1258.  
  1259.         else:
  1260.             if self.cur_device.status_type == STATUS_TYPE_NONE:
  1261.                 self.drawStatusLCD(self.__tr("Status information not"), self.__tr("available for this device."))
  1262.  
  1263.             elif not self.cur_device.supported:
  1264.                 self.drawStatusLCD(self.__tr("Device not supported."))
  1265.  
  1266.             elif not self.cur_device.hist:
  1267.                 self.drawStatusLCD(self.__tr("No status history available."))
  1268.  
  1269.             else:
  1270.                 self.drawStatusLCD()
  1271.  
  1272.  
  1273.     def drawStatusLCD(self, line1='', line2=''):
  1274.         pm = load_pixmap('panel_lcd', 'other')
  1275.  
  1276.         p = QPainter()
  1277.         p.begin(pm)
  1278.         p.setPen(QColor(0, 0, 0))
  1279.         p.setFont(self.font())
  1280.  
  1281.         x, y_line1, y_line2 = 10, 17, 33
  1282.  
  1283.         # TODO: Scroll long lines
  1284.         if line1:
  1285.             p.drawText(x, y_line1, line1)
  1286.  
  1287.         if line2:
  1288.             p.drawText(x, y_line2, line2)
  1289.  
  1290.         p.end()
  1291.  
  1292.         self.LCD.setPixmap(pm)
  1293.  
  1294.  
  1295.  
  1296.     def updateStatusTable(self):
  1297.         self.StatusTable.clear()
  1298.         flags = Qt.ItemIsSelectable | Qt.ItemIsEnabled
  1299.  
  1300.         row = 0
  1301.         hist = self.cur_device.hist[:]
  1302.  
  1303.         if hist:
  1304.             self.StatusTable.setRowCount(len(hist))
  1305.             self.StatusTable.setColumnCount(len(self.status_headers))
  1306.             self.StatusTable.setHorizontalHeaderLabels(self.status_headers)
  1307.             self.StatusTable.verticalHeader().hide()
  1308.             self.StatusTable.horizontalHeader().show()
  1309.  
  1310.             hist.reverse()
  1311.             row = len(hist)-1
  1312.  
  1313.             for e in hist:
  1314.                 if e is None:
  1315.                     continue
  1316.  
  1317.                 ess = device.queryString(e.event_code, 0)
  1318.                 esl = device.queryString(e.event_code, 1)
  1319.  
  1320.                 if row == 0:
  1321.                     desc = self.__tr("(most recent)")
  1322.  
  1323.                 else:
  1324.                     desc = getTimeDeltaDesc(e.timedate)
  1325.  
  1326.                 dt = QDateTime()
  1327.                 dt.setTime_t(int(e.timedate)) #, Qt.LocalTime)
  1328.  
  1329.                 # TODO: In Qt4.x, use QLocale.toString(date, format)
  1330.                 tt = QString("%1 %2").arg(dt.toString()).arg(desc)
  1331.  
  1332.                 if e.job_id:
  1333.                     job_id = unicode(e.job_id)
  1334.                 else:
  1335.                     job_id = u''
  1336.  
  1337.                 error_state = STATUS_TO_ERROR_STATE_MAP.get(e.event_code, ERROR_STATE_CLEAR)
  1338.                 tech_type = self.cur_device.tech_type
  1339.  
  1340.                 if tech_type in (TECH_TYPE_COLOR_INK, TECH_TYPE_MONO_INK):
  1341.                     status_pix = getStatusListIcon(error_state)[0] # ink
  1342.                 else:
  1343.                     status_pix = getStatusListIcon(error_state)[1] # laser
  1344.  
  1345.                 event_code = unicode(e.event_code)
  1346.  
  1347.                 i = QTableWidgetItem(QIcon(status_pix), self.__tr(""))
  1348.                 i.setFlags(flags)
  1349.                 self.StatusTable.setItem(row, 0, i)
  1350.  
  1351.                 for col, t in [(1, ess), (2, tt), (3, event_code), (4, job_id), (5, esl)]:
  1352.                     i = QTableWidgetItem(QString(t))
  1353.                     i.setFlags(flags)
  1354.  
  1355.                     self.StatusTable.setItem(row, col, i)
  1356.  
  1357.                 row -= 1
  1358.  
  1359.             self.StatusTable.resizeColumnsToContents()
  1360.             self.StatusTable.setColumnWidth(0, 24)
  1361.  
  1362.         else:
  1363.             self.StatusTable.setRowCount(1)
  1364.             self.StatusTable.setColumnCount(2)
  1365.             self.StatusTable.setHorizontalHeaderLabels(["", ""])
  1366.             self.StatusTable.verticalHeader().hide()
  1367.             self.StatusTable.horizontalHeader().hide()
  1368.  
  1369.             flags = Qt.ItemIsEnabled
  1370.  
  1371.             pixmap = getStatusListIcon(ERROR_STATE_ERROR)[0]
  1372.             i = QTableWidgetItem(QIcon(pixmap), self.__tr(""))
  1373.             i.setFlags(flags)
  1374.             self.StatusTable.setItem(row, 0, i)
  1375.  
  1376.             i = QTableWidgetItem(self.__tr("Status information not available for this device."))
  1377.             i.setFlags(flags)
  1378.             self.StatusTable.setItem(0, 1, i)
  1379.  
  1380.             self.StatusTable.resizeColumnsToContents()
  1381.             self.StatusTable.setColumnWidth(0, 24)
  1382.  
  1383.  
  1384.     # ***********************************************************************************
  1385.     #
  1386.     # SUPPLIES TAB
  1387.     #
  1388.     # ***********************************************************************************
  1389.  
  1390.     def initSuppliesTab(self):
  1391.         self.pix_battery = load_pixmap('battery', '16x16')
  1392.  
  1393.         yellow = "#ffff00"
  1394.         light_yellow = "#ffffcc"
  1395.         cyan = "#00ffff"
  1396.         light_cyan = "#ccffff"
  1397.         magenta = "#ff00ff"
  1398.         light_magenta = "#ffccff"
  1399.         black = "#000000"
  1400.         blue = "#0000ff"
  1401.         dark_grey = "#808080"
  1402.         light_grey = "#c0c0c0"
  1403.  
  1404.         self.TYPE_TO_PIX_MAP = {
  1405.                                AGENT_TYPE_UNSPECIFIED : [black],
  1406.                                AGENT_TYPE_BLACK: [black],
  1407.                                AGENT_TYPE_CMY: [cyan, magenta, yellow],
  1408.                                AGENT_TYPE_KCM: [light_cyan, light_magenta, light_yellow],
  1409.                                AGENT_TYPE_GGK: [dark_grey],
  1410.                                AGENT_TYPE_YELLOW: [yellow],
  1411.                                AGENT_TYPE_MAGENTA: [magenta],
  1412.                                AGENT_TYPE_CYAN : [cyan],
  1413.                                AGENT_TYPE_CYAN_LOW: [light_cyan],
  1414.                                AGENT_TYPE_YELLOW_LOW: [light_yellow],
  1415.                                AGENT_TYPE_MAGENTA_LOW: [light_magenta],
  1416.                                AGENT_TYPE_BLUE: [blue],
  1417.                                AGENT_TYPE_KCMY_CM: [yellow, cyan, magenta],
  1418.                                AGENT_TYPE_LC_LM: [light_cyan, light_magenta],
  1419.                                #AGENT_TYPE_Y_M: [yellow, magenta],
  1420.                                #AGENT_TYPE_C_K: [black, cyan],
  1421.                                AGENT_TYPE_LG_PK: [light_grey, dark_grey],
  1422.                                AGENT_TYPE_LG: [light_grey],
  1423.                                AGENT_TYPE_G: [dark_grey],
  1424.                                AGENT_TYPE_PG: [light_grey],
  1425.                                AGENT_TYPE_C_M: [cyan, magenta],
  1426.                                AGENT_TYPE_K_Y: [black, yellow],
  1427.                                }
  1428.  
  1429.         self.supplies_headers = [self.__tr(""), self.__tr("Description"),
  1430.                                  self.__tr("HP Part No."), self.__tr("Approx. Level"),
  1431.                                  self.__tr("Status")]
  1432.  
  1433.  
  1434.     def updateSuppliesTab(self):
  1435.         beginWaitCursor()
  1436.         flags = Qt.ItemIsSelectable | Qt.ItemIsEnabled
  1437.  
  1438.         try:
  1439.             self.SuppliesTable.clear()
  1440.             self.SuppliesTable.setRowCount(0)
  1441.             self.SuppliesTable.setColumnCount(0)
  1442.  
  1443.             if self.cur_device is not None and \
  1444.                 self.cur_device.supported and \
  1445.                 self.cur_device.status_type != STATUS_TYPE_NONE and \
  1446.                 self.cur_device.device_state != DEVICE_STATE_NOT_FOUND:
  1447.  
  1448.                 try:
  1449.                     self.cur_device.sorted_supplies
  1450.                 except AttributeError:
  1451.                     self.cur_device.sorted_supplies = []
  1452.  
  1453.                 if not self.cur_device.sorted_supplies:
  1454.                     a = 1
  1455.                     while True:
  1456.                         try:
  1457.                             agent_type = int(self.cur_device.dq['agent%d-type' % a])
  1458.                             agent_kind = int(self.cur_device.dq['agent%d-kind' % a])
  1459.                             agent_sku = self.cur_device.dq['agent%d-sku' % a]
  1460.                         except KeyError:
  1461.                             break
  1462.                         else:
  1463.                             self.cur_device.sorted_supplies.append((a, agent_kind, agent_type, agent_sku))
  1464.  
  1465.                         a += 1
  1466.  
  1467.                     self.cur_device.sorted_supplies.sort(lambda x, y: cmp(x[1], y[1]) or cmp(x[3], y[3]))
  1468.  
  1469.                 self.SuppliesTable.setRowCount(len(self.cur_device.sorted_supplies))
  1470.                 self.SuppliesTable.setColumnCount(len(self.supplies_headers))
  1471.                 self.SuppliesTable.setHorizontalHeaderLabels(self.supplies_headers)
  1472.                 self.SuppliesTable.verticalHeader().hide()
  1473.                 self.SuppliesTable.horizontalHeader().show()
  1474.                 self.SuppliesTable.setIconSize(QSize(100, 18))
  1475.  
  1476.                 for row, x in enumerate(self.cur_device.sorted_supplies):
  1477.                     a, agent_kind, agent_type, agent_sku = x
  1478.                     agent_level = int(self.cur_device.dq['agent%d-level' % a])
  1479.                     agent_desc = self.cur_device.dq['agent%d-desc' % a]
  1480.                     agent_health_desc = self.cur_device.dq['agent%d-health-desc' % a]
  1481.  
  1482.                     # Bar graph level
  1483.                     level_pixmap = None
  1484.                     if agent_kind in (AGENT_KIND_SUPPLY,
  1485.                                       #AGENT_KIND_HEAD,
  1486.                                       AGENT_KIND_HEAD_AND_SUPPLY,
  1487.                                       AGENT_KIND_TONER_CARTRIDGE,
  1488.                                       AGENT_KIND_MAINT_KIT,
  1489.                                       AGENT_KIND_ADF_KIT,
  1490.                                       AGENT_KIND_INT_BATTERY,
  1491.                                       AGENT_KIND_DRUM_KIT,
  1492.                                       ):
  1493.  
  1494.                         level_pixmap = self.createStatusLevelGraphic(agent_level, agent_type)
  1495.  
  1496.                     # Color icon
  1497.                     pixmap = None
  1498.                     if agent_kind in (AGENT_KIND_SUPPLY,
  1499.                                       AGENT_KIND_HEAD,
  1500.                                       AGENT_KIND_HEAD_AND_SUPPLY,
  1501.                                       AGENT_KIND_TONER_CARTRIDGE,
  1502.                                       #AGENT_KIND_MAINT_KIT,
  1503.                                       #AGENT_KIND_ADF_KIT,
  1504.                                       AGENT_KIND_INT_BATTERY,
  1505.                                       #AGENT_KIND_DRUM_KIT,
  1506.                                       ):
  1507.  
  1508.                         pixmap = self.getStatusIcon(agent_kind, agent_type)
  1509.  
  1510.                     if pixmap is not None:
  1511.                         i = QTableWidgetItem(QIcon(pixmap), self.__tr(""))
  1512.                         i.setFlags(flags)
  1513.                         self.SuppliesTable.setItem(row, 0, i)
  1514.  
  1515.                     for col, t in [(1, agent_desc), (2, agent_sku), (4, agent_health_desc)]:
  1516.                         i = QTableWidgetItem(QString(t))
  1517.                         i.setFlags(flags)
  1518.                         self.SuppliesTable.setItem(row, col, i)
  1519.  
  1520.                     if level_pixmap is not None:
  1521.                         i = QTableWidgetItem(QIcon(level_pixmap), self.__tr(""))
  1522.                         i.setFlags(flags)
  1523.                         self.SuppliesTable.setItem(row, 3, i)
  1524.  
  1525.                 self.SuppliesTable.resizeColumnsToContents()
  1526.                 self.SuppliesTable.setColumnWidth(0, 24)
  1527.                 self.SuppliesTable.setColumnWidth(3, 120)
  1528.  
  1529.             else: # No supplies info
  1530.                 log.warning("Supplies information not available for this device.")
  1531.                 flags = Qt.ItemIsEnabled
  1532.                 self.SuppliesTable.setRowCount(1)
  1533.                 self.SuppliesTable.setColumnCount(2)
  1534.                 self.SuppliesTable.setHorizontalHeaderLabels(["", ""])
  1535.                 self.SuppliesTable.verticalHeader().hide()
  1536.                 self.SuppliesTable.horizontalHeader().hide()
  1537.  
  1538.                 i = QTableWidgetItem(self.__tr("Supplies information not available for this device."))
  1539.                 i.setFlags(flags)
  1540.                 self.SuppliesTable.setItem(0, 1, i)
  1541.  
  1542.                 pixmap = getStatusListIcon(ERROR_STATE_ERROR)[0]
  1543.                 i = QTableWidgetItem(QIcon(pixmap), self.__tr(""))
  1544.                 i.setFlags(flags)
  1545.                 self.SuppliesTable.setItem(0, 0, i)
  1546.  
  1547.                 self.SuppliesTable.resizeColumnsToContents()
  1548.                 self.SuppliesTable.setColumnWidth(0, 24)
  1549.  
  1550.         finally:
  1551.             endWaitCursor()
  1552.  
  1553.  
  1554.     def getStatusIcon(self, agent_kind, agent_type):
  1555.         if agent_kind in (AGENT_KIND_SUPPLY,
  1556.                           AGENT_KIND_HEAD,
  1557.                           AGENT_KIND_HEAD_AND_SUPPLY,
  1558.                           AGENT_KIND_TONER_CARTRIDGE):
  1559.  
  1560.             map = self.TYPE_TO_PIX_MAP[agent_type]
  1561.  
  1562.             if isinstance(map, list):
  1563.                 map_len = len(map)
  1564.                 pix = QPixmap(16, 16)
  1565.                 pix.fill(QColor(0, 0, 0, 0))
  1566.                 p = QPainter()
  1567.  
  1568.                 p.begin(pix)
  1569.                 p.setRenderHint(QPainter.Antialiasing)
  1570.  
  1571.                 if map_len == 1:
  1572.                     p.setPen(QColor(map[0]))
  1573.                     p.setBrush(QBrush(QColor(map[0]), Qt.SolidPattern))
  1574.                     p.drawPie(2, 2, 10, 10, 0, 5760)
  1575.  
  1576.                 elif map_len == 2:
  1577.                     p.setPen(QColor(map[0]))
  1578.                     p.setBrush(QBrush(QColor(map[0]), Qt.SolidPattern))
  1579.                     p.drawPie(2, 4, 8, 8, 0, 5760)
  1580.  
  1581.                     p.setPen(QColor(map[1]))
  1582.                     p.setBrush(QBrush(QColor(map[1]), Qt.SolidPattern))
  1583.                     p.drawPie(6, 4, 8, 8, 0, 5760)
  1584.  
  1585.                 elif map_len == 3:
  1586.                     p.setPen(QColor(map[2]))
  1587.                     p.setBrush(QBrush(QColor(map[2]), Qt.SolidPattern))
  1588.                     p.drawPie(6, 6, 8, 8, 0, 5760)
  1589.  
  1590.                     p.setPen(QColor(map[1]))
  1591.                     p.setBrush(QBrush(QColor(map[1]), Qt.SolidPattern))
  1592.                     p.drawPie(2, 6, 8, 8, 0, 5760)
  1593.  
  1594.                     p.setPen(QColor(map[0]))
  1595.                     p.setBrush(QBrush(QColor(map[0]), Qt.SolidPattern))
  1596.                     p.drawPie(4, 2, 8, 8, 0, 5760)
  1597.  
  1598.                 p.end()
  1599.                 return pix
  1600.  
  1601.             else:
  1602.                 return map
  1603.  
  1604.         elif agent_kind == AGENT_KIND_INT_BATTERY:
  1605.                 return self.pix_battery
  1606.  
  1607.  
  1608.     def createStatusLevelGraphic(self, percent, agent_type, w=100, h=18):
  1609.         if percent:
  1610.             fw = w/100*percent
  1611.         else:
  1612.             fw = 0
  1613.  
  1614.         px = QPixmap(w, h)
  1615.         px.fill(QColor(0, 0, 0, 0))
  1616.         pp = QPainter()
  1617.         pp.begin(px)
  1618.         pp.setRenderHint(QPainter.Antialiasing)
  1619.         pp.setPen(Qt.black)
  1620.  
  1621.         map = self.TYPE_TO_PIX_MAP[agent_type]
  1622.         map_len = len(map)
  1623.  
  1624.         if map_len == 1 or map_len > 3:
  1625.             pp.fillRect(0, 0, fw, h, QBrush(QColor(map[0])))
  1626.  
  1627.         elif map_len == 2:
  1628.             h2 = h / 2
  1629.             pp.fillRect(0, 0, fw, h2, QBrush(QColor(map[0])))
  1630.             pp.fillRect(0, h2, fw, h, QBrush(QColor(map[1])))
  1631.  
  1632.         elif map_len == 3:
  1633.             h3 = h / 3
  1634.             h23 = 2 * h3
  1635.             pp.fillRect(0, 0, fw, h3, QBrush(QColor(map[0])))
  1636.             pp.fillRect(0, h3, fw, h23, QBrush(QColor(map[1])))
  1637.             pp.fillRect(0, h23, fw, h, QBrush(QColor(map[2])))
  1638.  
  1639.         # draw black frame
  1640.         pp.drawRect(0, 0, w, h)
  1641.  
  1642.         if percent > 75 and agent_type in \
  1643.           (AGENT_TYPE_BLACK, AGENT_TYPE_UNSPECIFIED, AGENT_TYPE_BLUE):
  1644.             pp.setPen(Qt.white)
  1645.  
  1646.         # 75% ticks
  1647.         w1 = 3 * w / 4
  1648.         h6 = h / 6
  1649.         pp.drawLine(w1, 0, w1, h6)
  1650.         pp.drawLine(w1, h, w1, h-h6)
  1651.  
  1652.         if percent > 50 and agent_type in \
  1653.           (AGENT_TYPE_BLACK, AGENT_TYPE_UNSPECIFIED, AGENT_TYPE_BLUE):
  1654.             pp.setPen(Qt.white)
  1655.  
  1656.         # 50% ticks
  1657.         w2 = w / 2
  1658.         h4 = h / 4
  1659.         pp.drawLine(w2, 0, w2, h4)
  1660.         pp.drawLine(w2, h, w2, h-h4)
  1661.  
  1662.         if percent > 25 and agent_type in \
  1663.           (AGENT_TYPE_BLACK, AGENT_TYPE_UNSPECIFIED, AGENT_TYPE_BLUE):
  1664.             pp.setPen(Qt.white)
  1665.  
  1666.         # 25% ticks
  1667.         w4 = w / 4
  1668.         pp.drawLine(w4, 0, w4, h6)
  1669.         pp.drawLine(w4, h, w4, h-h6)
  1670.  
  1671.         pp.end()
  1672.  
  1673.         return px
  1674.  
  1675.  
  1676.  
  1677.     # ***********************************************************************************
  1678.     #
  1679.     # PRINTER SETTINGS TAB
  1680.     #
  1681.     # ***********************************************************************************
  1682.  
  1683.     def initPrintSettingsTab(self):
  1684.         pass
  1685.  
  1686.  
  1687.     def updatePrintSettingsTab(self):
  1688.         beginWaitCursor()
  1689.         try:
  1690.             if self.cur_device.device_type == DEVICE_TYPE_PRINTER:
  1691.                 self.PrintSettingsPrinterNameLabel.setText(self.__tr("Printer Name:"))
  1692.             else:
  1693.                 self.PrintSettingsPrinterNameLabel.setText(self.__tr("Fax Name:"))
  1694.  
  1695.             self.PrintSettingsToolbox.updateUi(self.cur_device, self.cur_printer)
  1696.         finally:
  1697.             endWaitCursor()
  1698.  
  1699.  
  1700.     # ***********************************************************************************
  1701.     #
  1702.     # PRINTER CONTROL TAB
  1703.     #
  1704.     # ***********************************************************************************
  1705.  
  1706.     def initPrintControlTab(self):
  1707.         self.JOB_STATES = { cups.IPP_JOB_PENDING : self.__tr("Pending"),
  1708.                             cups.IPP_JOB_HELD : self.__tr("On hold"),
  1709.                             cups.IPP_JOB_PROCESSING : self.__tr("Printing"),
  1710.                             cups.IPP_JOB_STOPPED : self.__tr("Stopped"),
  1711.                             cups.IPP_JOB_CANCELLED : self.__tr("Canceled"),
  1712.                             cups.IPP_JOB_ABORTED : self.__tr("Aborted"),
  1713.                             cups.IPP_JOB_COMPLETED : self.__tr("Completed"),
  1714.                            }
  1715.  
  1716.         self.CancelJobButton.setIcon(QIcon(load_pixmap('cancel', '16x16')))
  1717.         self.RefreshButton.setIcon(QIcon(load_pixmap('refresh', '16x16')))
  1718.  
  1719.         self.JOB_STATE_ICONS = { cups.IPP_JOB_PENDING: QIcon(load_pixmap("busy", "16x16")),
  1720.                                  cups.IPP_JOB_HELD : QIcon(load_pixmap("busy", "16x16")),
  1721.                                  cups.IPP_JOB_PROCESSING : QIcon(load_pixmap("print", "16x16")),
  1722.                                  cups.IPP_JOB_STOPPED : QIcon(load_pixmap("warning", "16x16")),
  1723.                                  cups.IPP_JOB_CANCELLED : QIcon(load_pixmap("warning", "16x16")),
  1724.                                  cups.IPP_JOB_ABORTED : QIcon(load_pixmap("error", "16x16")),
  1725.                                  cups.IPP_JOB_COMPLETED : QIcon(load_pixmap("ok", "16x16")),
  1726.                                 }
  1727.  
  1728.         self.connect(self.StartStopButton, SIGNAL("clicked()"), self.StartStopButton_clicked)
  1729.         self.connect(self.AcceptRejectButton, SIGNAL("clicked()"), self.AcceptRejectButton_clicked)
  1730.         self.connect(self.SetDefaultButton, SIGNAL("clicked()"), self.SetDefaultButton_clicked)
  1731.         self.connect(self.CancelJobButton, SIGNAL("clicked()"), self.CancelJobButton_clicked)
  1732.         self.connect(self.RefreshButton, SIGNAL("clicked()"), self.RefreshButton_clicked)
  1733.  
  1734.         self.job_headers = [self.__tr("Status"), self.__tr("Title/Description"), self.__tr("Job ID")]
  1735.  
  1736.         # TODO: Check queues at startup and send events if stopped or rejecting
  1737.  
  1738.  
  1739.     def CancelJobButton_clicked(self):
  1740.         item = self.JobTable.currentItem()
  1741.  
  1742.         if item is not None:
  1743.             job_id, ok = item.data(Qt.UserRole).toInt()
  1744.             if ok and job_id:
  1745.                self.cur_device.cancelJob(job_id)
  1746.                QTimer.singleShot(1000, self.updatePrintControlTab)
  1747.  
  1748.  
  1749.     def RefreshButton_clicked(self):
  1750.         self.updatePrintControlTab()
  1751.  
  1752.  
  1753.     def updatePrintControlTab(self):
  1754.         if self.cur_device.device_type == DEVICE_TYPE_PRINTER:
  1755.             self.PrintControlPrinterNameLabel.setText(self.__tr("Printer Name:"))
  1756.         else:
  1757.             self.PrintControlPrinterNameLabel.setText(self.__tr("Fax Name:"))
  1758.  
  1759.         self.JobTable.clear()
  1760.         self.JobTable.setRowCount(0)
  1761.         self.JobTable.setColumnCount(0)
  1762.         self.updatePrintController()
  1763.         flags = Qt.ItemIsSelectable | Qt.ItemIsEnabled
  1764.         jobs = cups.getJobs()
  1765.         num_jobs = 0
  1766.         for j in jobs:
  1767.             if j.dest.decode('utf-8') == unicode(self.cur_printer):
  1768.                 num_jobs += 1
  1769.  
  1770.         if num_jobs:
  1771.             self.CancelJobButton.setEnabled(True)
  1772.             self.JobTable.setRowCount(num_jobs)
  1773.             self.JobTable.setColumnCount(len(self.job_headers))
  1774.             self.JobTable.setHorizontalHeaderLabels(self.job_headers)
  1775.  
  1776.             for row, j in enumerate(jobs):
  1777.                 if j.dest.decode('utf-8') == unicode(self.cur_printer):
  1778.                     i = QTableWidgetItem(self.JOB_STATE_ICONS[j.state], self.JOB_STATES[j.state])
  1779.                     i.setData(Qt.UserRole, QVariant(j.id))
  1780.                     i.setFlags(flags)
  1781.                     self.JobTable.setItem(row, 0, i)
  1782.  
  1783.                     i = QTableWidgetItem(j.title)
  1784.                     i.setFlags(flags)
  1785.                     self.JobTable.setItem(row, 1, i)
  1786.  
  1787.                     i = QTableWidgetItem(unicode(j.id))
  1788.                     i.setFlags(flags)
  1789.                     self.JobTable.setItem(row, 2, i)
  1790.  
  1791.  
  1792.             self.JobTable.setCurrentCell(0, 0)
  1793.             self.JobTable.resizeColumnsToContents()
  1794.  
  1795.         else:
  1796.             self.CancelJobButton.setEnabled(False)
  1797.  
  1798.  
  1799.     def getPrinterState(self):
  1800.         self.printer_state = cups.IPP_PRINTER_STATE_IDLE
  1801.         self.printer_accepting = True
  1802.         cups_printers = cups.getPrinters()
  1803.  
  1804.         for p in cups_printers:
  1805.             if p.name.decode('utf-8') == self.cur_printer:
  1806.                 self.printer_state = p.state
  1807.                 self.printer_accepting = p.accepting
  1808.                 break
  1809.  
  1810.  
  1811.     def updatePrintController(self):
  1812.         # default printer
  1813.         self.SetDefaultButton.setText(self.__tr("Set as Default"))
  1814.  
  1815.         default_printer = cups.getDefaultPrinter()
  1816.         if default_printer is not None:
  1817.             default_printer = default_printer.decode('utf8')
  1818.  
  1819.         if default_printer == self.cur_printer:
  1820.             self.SetDefaultLabel.setText(self.__tr("Default Printer"))
  1821.             self.SetDefaultIcon.setPixmap(load_pixmap("ok", "16x16"))
  1822.             self.SetDefaultButton.setEnabled(False)
  1823.  
  1824.         else:
  1825.             self.SetDefaultLabel.setText(self.__tr("Not Default Printer"))
  1826.             self.SetDefaultIcon.setPixmap(load_pixmap("info", "16x16"))
  1827.             self.SetDefaultButton.setEnabled(True)
  1828.  
  1829.         self.getPrinterState()
  1830.  
  1831.         # start/stop
  1832.         if self.printer_state == cups.IPP_PRINTER_STATE_IDLE:
  1833.             self.StartStopLabel.setText(self.__tr("Started/Idle"))
  1834.             self.StartStopIcon.setPixmap(load_pixmap("idle", "16x16"))
  1835.  
  1836.             if self.cur_device.device_type == DEVICE_TYPE_PRINTER:
  1837.                 self.StartStopButton.setText(self.__tr("Stop Printer"))
  1838.  
  1839.             else:
  1840.                 self.StartStopButton.setText(self.__tr("Stop Fax"))
  1841.  
  1842.         elif self.printer_state == cups.IPP_PRINTER_STATE_PROCESSING:
  1843.             self.StartStopLabel.setText(self.__tr("Started/Processing"))
  1844.             self.StartStopIcon.setPixmap(load_pixmap("busy", "16x16"))
  1845.  
  1846.             if self.cur_device.device_type == DEVICE_TYPE_PRINTER:
  1847.                 self.StartStopButton.setText(self.__tr("Stop Printer"))
  1848.  
  1849.             else:
  1850.                 self.StartStopButton.setText(self.__tr("Stop Fax"))
  1851.         else:
  1852.             self.StartStopLabel.setText(self.__tr("Stopped"))
  1853.             self.StartStopIcon.setPixmap(load_pixmap("warning", "16x16"))
  1854.  
  1855.             if self.cur_device.device_type == DEVICE_TYPE_PRINTER:
  1856.                 self.StartStopButton.setText(self.__tr("Start Printer"))
  1857.  
  1858.             else:
  1859.                 self.StartStopButton.setText(self.__tr("Start Fax"))
  1860.  
  1861.         # reject/accept
  1862.         if self.printer_accepting:
  1863.             self.AcceptRejectLabel.setText(self.__tr("Accepting Jobs"))
  1864.             self.AcceptRejectIcon.setPixmap(load_pixmap("idle", "16x16"))
  1865.             self.AcceptRejectButton.setText(self.__tr("Reject Jobs"))
  1866.  
  1867.         else:
  1868.             self.AcceptRejectLabel.setText(self.__tr("Rejecting Jobs"))
  1869.             self.AcceptRejectIcon.setPixmap(load_pixmap("warning", "16x16"))
  1870.             self.AcceptRejectButton.setText(self.__tr("Accept Jobs"))
  1871.  
  1872.  
  1873.  
  1874.     def StartStopButton_clicked(self):
  1875.         beginWaitCursor()
  1876.         try:
  1877.             if self.printer_state in (cups.IPP_PRINTER_STATE_IDLE, cups.IPP_PRINTER_STATE_PROCESSING):
  1878.                 result = cups.stop(self.cur_printer)
  1879.                 if result:
  1880.                     if self.cur_device.device_type == DEVICE_TYPE_PRINTER:
  1881.                         e = EVENT_PRINTER_QUEUE_STOPPED
  1882.                     else:
  1883.                         e = EVENT_FAX_QUEUE_STOPPED
  1884.  
  1885.             else:
  1886.                 result = cups.start(self.cur_printer)
  1887.                 if result:
  1888.                     if self.cur_device.device_type == DEVICE_TYPE_PRINTER:
  1889.                         e = EVENT_PRINTER_QUEUE_STARTED
  1890.                     else:
  1891.                         e = EVENT_FAX_QUEUE_STARTED
  1892.  
  1893.             if result:
  1894.                 self.updatePrintController()
  1895.                 self.cur_device.sendEvent(e, self.cur_printer)
  1896.             else:
  1897.                 log.error("Start/Stop printer operation failed")
  1898.  
  1899.         finally:
  1900.             endWaitCursor()
  1901.  
  1902.  
  1903.  
  1904.     def AcceptRejectButton_clicked(self):
  1905.         beginWaitCursor()
  1906.         try:
  1907.             if self.printer_accepting:
  1908.                 result = cups.reject(self.cur_printer)
  1909.                 if result:
  1910.                     if self.cur_device.device_type == DEVICE_TYPE_PRINTER:
  1911.                         e = EVENT_PRINTER_QUEUE_REJECTING_JOBS
  1912.                     else:
  1913.                         e = EVENT_FAX_QUEUE_REJECTING_JOBS
  1914.  
  1915.             else:
  1916.                 result = cups.accept(self.cur_printer)
  1917.                 if result:
  1918.                     if self.cur_device.device_type == DEVICE_TYPE_PRINTER:
  1919.                         e = EVENT_PRINTER_QUEUE_ACCEPTING_JOBS
  1920.                     else:
  1921.                         e = EVENT_FAX_QUEUE_ACCEPTING_JOBS
  1922.  
  1923.             if result:
  1924.                 self.updatePrintController()
  1925.                 self.cur_device.sendEvent(e, self.cur_printer)
  1926.             else:
  1927.                 log.error("Reject/Accept jobs operation failed")
  1928.  
  1929.  
  1930.         finally:
  1931.             endWaitCursor()
  1932.  
  1933.  
  1934.  
  1935.     def SetDefaultButton_clicked(self):
  1936.         beginWaitCursor()
  1937.         try:
  1938.             result = cups.setDefaultPrinter(self.cur_printer.encode('utf8'))
  1939.             if not result:
  1940.                 log.error("Set default printer failed.")
  1941.             else:
  1942.                 self.updatePrintController()
  1943.                 if self.cur_device.device_type == DEVICE_TYPE_PRINTER:
  1944.                     e = EVENT_PRINTER_QUEUE_SET_AS_DEFAULT
  1945.                 else:
  1946.                     e = EVENT_FAX_QUEUE_SET_AS_DEFAULT
  1947.  
  1948.                 self.cur_device.sendEvent(e, self.cur_printer)
  1949.  
  1950.         finally:
  1951.             endWaitCursor()
  1952.  
  1953.  
  1954.  
  1955.     def cancelCheckedJobs(self):
  1956.         beginWaitCursor()
  1957.         try:
  1958.             item = self.JobTable.firstChild()
  1959.             while item is not None:
  1960.                 if item.isOn():
  1961.                     self.cur_device.cancelJob(item.job_id)
  1962.  
  1963.                 item = item.nextSibling()
  1964.  
  1965.         finally:
  1966.             endWaitCursor()
  1967.  
  1968.  
  1969.         self.updatePrintControlTab()
  1970.  
  1971.  
  1972.  
  1973.  
  1974.     # ***********************************************************************************
  1975.     #
  1976.     # EXIT/CHILD CLEANUP
  1977.     #
  1978.     # ***********************************************************************************
  1979.  
  1980.     def closeEvent(self, event):
  1981.         self.cleanup()
  1982.         event.accept()
  1983.  
  1984.  
  1985.     def cleanup(self):
  1986.         self.cleanupChildren()
  1987.  
  1988.  
  1989.     def cleanupChildren(self):
  1990.         log.debug("Cleaning up child processes.")
  1991.         try:
  1992.             os.waitpid(-1, os.WNOHANG)
  1993.         except OSError:
  1994.             pass
  1995.  
  1996.  
  1997.     def quit(self):
  1998.         self.cleanupChildren()
  1999.         self.close()
  2000.  
  2001.  
  2002.     # ***********************************************************************************
  2003.     #
  2004.     # DEVICE SETTINGS PLUGIN
  2005.     #
  2006.     # ***********************************************************************************
  2007.  
  2008.  
  2009.     # ***********************************************************************************
  2010.     #
  2011.     # SETTINGS DIALOG
  2012.     #
  2013.     # ***********************************************************************************
  2014.  
  2015.     def PreferencesAction_activated(self, tab_to_show=0):
  2016.         dlg = SettingsDialog(self)
  2017.         dlg.TabWidget.setCurrentIndex(tab_to_show)
  2018.  
  2019.         if dlg.exec_() == QDialog.Accepted:
  2020.             self.user_settings.load()
  2021.  
  2022.             self.cur_device.sendEvent(EVENT_USER_CONFIGURATION_CHANGED, self.cur_printer)
  2023.  
  2024.  
  2025.     # ***********************************************************************************
  2026.     #
  2027.     # SETUP/REMOVE
  2028.     #
  2029.     # ***********************************************************************************
  2030.  
  2031.     def SetupDeviceAction_activated(self):
  2032.         if utils.which('hp-setup'):
  2033.             cmd = 'hp-setup --gui'
  2034.         else:
  2035.             cmd = 'python ./setup.py --gui'
  2036.  
  2037.         log.debug(cmd)
  2038.         utils.run(cmd, log_output=True, password_func=None, timeout=1)
  2039.         self.rescanDevices()
  2040.         self.updatePrinterCombos()
  2041.  
  2042.  
  2043.     def RemoveDeviceAction_activated(self):
  2044.         if utils.which('hp-setup'):
  2045.             cmd = 'hp-setup --gui --remove'
  2046.         else:
  2047.             cmd = 'python ./setup.py --gui --remove'
  2048.  
  2049.         if self.cur_device_uri is not None:
  2050.             cmd += ' --device=%s' % self.cur_device_uri
  2051.  
  2052.         log.debug(cmd)
  2053.         utils.run(cmd, log_output=True, password_func=None, timeout=1)
  2054.         self.rescanDevices()
  2055.         self.updatePrinterCombos()
  2056.  
  2057.  
  2058.     # ***********************************************************************************
  2059.     #
  2060.     # MISC
  2061.     #
  2062.     # ***********************************************************************************
  2063.  
  2064.     def runExternalCommand(self, cmd, macro_char='%'):
  2065.         beginWaitCursor()
  2066.  
  2067.         try:
  2068.             if len(cmd) == 0:
  2069.                 FailureUI(self.__tr("<p><b>Unable to run command. No command specified.</b><p>Use <pre>Configure...</pre> to specify a command to run."))
  2070.                 log.error("No command specified. Use settings to configure commands.")
  2071.             else:
  2072.                 log.debug("Run: %s %s (%s) %s" % ("*"*20, cmd, self.cur_device_uri, "*"*20))
  2073.                 log.debug(cmd)
  2074.  
  2075.                 try:
  2076.                     cmd = ''.join([self.cur_device.device_vars.get(x, x) \
  2077.            for x in cmd.split(macro_char)])
  2078.                 except AttributeError:
  2079.                     pass
  2080.  
  2081.                 log.debug(cmd)
  2082.  
  2083.                 path = cmd.split()[0]
  2084.                 args = cmd.split()
  2085.  
  2086.                 log.debug(path)
  2087.                 log.debug(args)
  2088.  
  2089.                 self.cleanupChildren()
  2090.                 os.spawnvp(os.P_NOWAIT, path, args)
  2091.                 qApp.processEvents()
  2092.  
  2093.         finally:
  2094.             endWaitCursor()
  2095.  
  2096.  
  2097.     def helpContents(self):
  2098.         utils.openURL(self.docs)
  2099.  
  2100.  
  2101.     def helpAbout(self):
  2102.         dlg = AboutDialog(self, prop.version, self.toolbox_version + " (Qt4)")
  2103.         dlg.exec_()
  2104.  
  2105.  
  2106.     def __tr(self,s,c = None):
  2107.         return qApp.translate("DevMgr5",s,c)
  2108.  
  2109.  
  2110. # XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  2111.  
  2112. class PasswordDialog(QDialog):
  2113.     def __init__(self, prompt, parent=None, name=None, modal=0, fl=0):
  2114.         QDialog.__init__(self, parent)
  2115.         self.prompt = prompt
  2116.  
  2117.         Layout= QGridLayout(self)
  2118.         Layout.setMargin(11)
  2119.         Layout.setSpacing(6)
  2120.  
  2121.         self.PromptTextLabel = QLabel(self)
  2122.         Layout.addWidget(self.PromptTextLabel,0,0,1,3)
  2123.  
  2124.         self.UsernameTextLabel = QLabel(self)
  2125.         Layout.addWidget(self.UsernameTextLabel,1,0)
  2126.  
  2127.         self.UsernameLineEdit = QLineEdit(self)
  2128.         self.UsernameLineEdit.setEchoMode(QLineEdit.Normal)
  2129.         Layout.addWidget(self.UsernameLineEdit,1,1,1,2)
  2130.  
  2131.         self.PasswordTextLabel = QLabel(self)
  2132.         Layout.addWidget(self.PasswordTextLabel,2,0)
  2133.  
  2134.         self.PasswordLineEdit = QLineEdit(self)
  2135.         self.PasswordLineEdit.setEchoMode(QLineEdit.Password)
  2136.         Layout.addWidget(self.PasswordLineEdit,2,1,1,2)
  2137.  
  2138.         self.OkPushButton = QPushButton(self)
  2139.         Layout.addWidget(self.OkPushButton,3,2)
  2140.  
  2141.         self.languageChange()
  2142.  
  2143.         self.resize(QSize(420,163).expandedTo(self.minimumSizeHint()))
  2144.  
  2145.         self.connect(self.OkPushButton, SIGNAL("clicked()"), self.accept)
  2146.         self.connect(self.PasswordLineEdit, SIGNAL("returnPressed()"), self.accept)
  2147.  
  2148.  
  2149.     def getUsername(self):
  2150.         return unicode(self.UsernameLineEdit.text())
  2151.  
  2152.  
  2153.     def getPassword(self):
  2154.         return unicode(self.PasswordLineEdit.text())
  2155.  
  2156.  
  2157.     def languageChange(self):
  2158.         self.setWindowTitle(self.__tr("HP Device Manager - Enter Username/Password"))
  2159.         self.PromptTextLabel.setText(self.__tr(self.prompt))
  2160.         self.UsernameTextLabel.setText(self.__tr("Username:"))
  2161.         self.PasswordTextLabel.setText(self.__tr("Password:"))
  2162.         self.OkPushButton.setText(self.__tr("OK"))
  2163.  
  2164.  
  2165.     def __tr(self,s,c = None):
  2166.         return qApp.translate("DevMgr5",s,c)
  2167.  
  2168. # XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  2169.  
  2170. def showPasswordUI(prompt):
  2171.     try:
  2172.         dlg = PasswordDialog(prompt, None)
  2173.  
  2174.         if dlg.exec_() == QDialog.Accepted:
  2175.             return (dlg.getUsername(), dlg.getPassword())
  2176.  
  2177.     finally:
  2178.         pass
  2179.  
  2180.     return ("", "")
  2181.  
  2182.  
  2183. def openEWS(host, zc):
  2184.     if zc:
  2185.         status, ip = hpmudext.get_zc_ip_address(zc)
  2186.         if status != hpmudext.HPMUD_R_OK:
  2187.             ip = "hplipopensource.com"
  2188.     else:
  2189.         ip = host
  2190.     return "http://%s" % ip
  2191.